{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# VAns: Variable Ansatz"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Overview"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Variational Quantum Algorithms (VQA) are about to tune parameters of quantum circuits to minimize an objective function of interest. The commonly used VQAs, like Variational Quantum Eigensolvers (VQE) and Quantum Approximate Optimization Algorithm (QAOA), perform optimization using a user-defined ansatz with fixed structure. However, if the ansatz is too simple or shallow, the expressivity of the circuit will not be insufficient to get the optimal value for the objective function. On the other side, if the ansatz is overly complicated or long, we may encounter barren plateau effect, which impedes us from obtaining the global minimum value. Thus, a circuit structure design search algorithm will be helpful to find the appropriate circuit for a specific task.\n",
    "\n",
    "In this tutorial, we will discuss a circuit architecture design search algorithm called Variable ansatz (VAns) [1]. Starting with an initial circuit, VAns keeps inserting and deleting gates during the optimization process in order to minimize the loss function as well as keep the circuit shallow. We will use this method to accomplish an adjusted version of VQE task and compare the resulting circuit with the original VQE circuit."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Pipeline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "VAns consists of the following steps:  \n",
    "1. Start with an initial circuit. Run the inner optimization, which is just the original VQE process, and get the optimal value. \n",
    "2. Randomly choose a block from the 'pool' (As the following figure shows, each block only consists of $R_y$, $R_z$, and $CNOT$ gates), and insert the block at the end of circuit. Qubits that the block is applied on are also uniformly sampled. The parameters of the inserted gates are initialized to $0$ so that the inserted circuit is equivalent to identity. \n",
    "\n",
    "![Inserting Blocks](blocks.png)\n",
    "\n",
    "3. Simplify the circuit according to the following rules. Run the optimization process, and get the optimal value of loss function. Compare the loss value to the previously stored one, decide whether to accept the new circuit or not according to a pre-defined threshold. If the circuit is accepted, remove gates that do not lower the loss, set the circuit as the current circuit and store the corresponding loss value.\n",
    "\n",
    "![Simplification rules](rules.png)\n",
    "\n",
    "4. Repeat steps 2-3 for chosen number of iterations, output the resulting circuit and the optimal loss value.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Paddle Quantum Implementation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We consider to get the ground state energy of Hydrogen molecule using [Variation Quantum Eigensolver (VQE)](https://qml.baidu.com/tutorials/quantum-simulation/variational-quantum-eigensolver.html) together with VAns to optimize the circuit structure as well. First, we import the required packages."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import paddle\n",
    "import paddle_quantum\n",
    "import paddle_quantum.qchem as qchem\n",
    "import numpy as np\n",
    "from paddle_quantum.ansatz import Circuit\n",
    "from paddle_quantum.ansatz.vans import Inserter, Simplifier, VAns, cir_decompose\n",
    "from paddle_quantum.hamiltonian import Hamiltonian\n",
    "\n",
    "np.random.seed(11)\n",
    "paddle.seed(11)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Before using the VAns algorithm, we need to get the Hamiltonian for Hydrogen. Details can be found in the VQE tutorial."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "geo = qchem.geometry(structure=[[\"H\", [-0.0, 0.0, 0.0]], [\"H\", [-0.0, 0.0, 0.74]]])\n",
    "# Save molecule information in to variable molecule, including one-body integrations, molecular and Hamiltonian\n",
    "molecule = qchem.get_molecular_data(\n",
    "    geometry=geo,\n",
    "    basis=\"sto-3g\",\n",
    "    charge=0,\n",
    "    multiplicity=1,\n",
    "    method=\"fci\",\n",
    "    if_save=True,\n",
    "    if_print=True,\n",
    ")\n",
    "# Recall Hamiltonian\n",
    "molecular_hamiltonian = qchem.spin_hamiltonian(\n",
    "    molecule=molecule,\n",
    "    filename=None,\n",
    "    multiplicity=1,\n",
    "    mapping_method=\"jordan_wigner\",\n",
    ")\n",
    "# n is the number of qubits\n",
    "n = molecular_hamiltonian.n_qubits"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Then we define the loss function, which is simply the expectation value of the Hamiltonian."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Define the loss function\n",
    "def loss_func(cir: Circuit, H: Hamiltonian) -> paddle.Tensor:\n",
    "    return cir().expec_val(H)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, we also need to set some training hyper-parameters before training."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "EPSI = 0.001 # set the epsilon\n",
    "IR = 1 # set the insertion rate\n",
    "ITERI = 120 # set the number of iterations used for VQE\n",
    "ITERO = 5 # set the number of iterations used for structure optimization\n",
    "LR = 0.1 # set the learning rate\n",
    "T = 0.01 # set the threshold\n",
    "A = 100 # set the accept wall\n",
    "IS0 = True # if the initial state is |0>, set to true\n",
    "\n",
    "paddle_quantum.set_backend('state_vector') # set the backend to state vector"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# initialize the framework\n",
    "vans = VAns(n, loss_func, molecular_hamiltonian,\n",
    "           epsilon=EPSI,\n",
    "           insert_rate=IR,\n",
    "           iter=ITERI,\n",
    "           iter_out=ITERO,\n",
    "           LR=LR,\n",
    "           threshold=T,\n",
    "           accept_wall=A,\n",
    "           zero_init_state=IS0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Initial Circuit"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We give a default initial circuit with uniformly sampled parameters. Then run the optimization process to get the optimal parameters in this initial circuit. The optimization process is the same as in the original VQE. Hyperparameters **iter** and **LR** are used in this process.The obtained loss function and the circuit with optimized parameters will be the initial point for the architecture optimization process. Note that the circuit is simplified when we decided to use this optimized circuit as the current starting point. Details of simplification can be found later."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "c:\\Users\\v_zhanglei48\\Anaconda3\\envs\\pq_new\\lib\\site-packages\\paddle\\fluid\\framework.py:744: DeprecationWarning: `np.bool` is a deprecated alias for the builtin `bool`. To silence this warning, use `bool` by itself. Doing this will not modify any behavior and is safe. If you specifically wanted the numpy scalar type, use `np.bool_` here.\n",
      "Deprecated in NumPy 1.20; for more details and guidance: https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations\n",
      "  elif dtype == np.bool:\n",
      "c:\\Users\\v_zhanglei48\\Anaconda3\\envs\\pq_new\\lib\\site-packages\\paddle\\tensor\\creation.py:130: DeprecationWarning: `np.object` is a deprecated alias for the builtin `object`. To silence this warning, use `object` by itself. Doing this will not modify any behavior and is safe. \n",
      "Deprecated in NumPy 1.20; for more details and guidance: https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations\n",
      "  if data.dtype == np.object:\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "iter: 20 loss: [-0.9960856]\n",
      "iter: 40 loss: [-1.0926806]\n",
      "iter: 60 loss: [-1.11364]\n",
      "iter: 80 loss: [-1.1163325]\n",
      "iter: 100 loss: [-1.1167175]\n",
      "iter: 120 loss: [-1.1167548]\n",
      "Current circuit is:\n",
      "--Rx(3.144)----Rz(3.512)----*----Rx(6.286)----Rz(3.039)-------------------------------------------------------------x--\n",
      "                            |                                                                                       |  \n",
      "----------------------------x----Rx(4.249)----Rz(3.141)----Rx(4.246)----Rz(5.477)----*------------------------------|--\n",
      "                                                                                     |                              |  \n",
      "--Rx(0.001)----Rz(5.499)----*--------------------------------------------------------x----Rx(3.141)----Rz(2.688)----|--\n",
      "                            |                                                                                       |  \n",
      "----------------------------x----Rx(0.003)----Rz(2.362)----Rx(0.003)----Rz(1.500)-----------------------------------*--\n",
      "                                                                                                                       \n"
     ]
    }
   ],
   "source": [
    "# Optimize the initial circuit\n",
    "itr_loss = vans.optimization(vans.cir) \n",
    "\n",
    "# Update the loss\n",
    "vans.loss = itr_loss\n",
    "\n",
    "# Print out the current circuit\n",
    "print(\"Current circuit is:\\n\" + str(vans.cir))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Insertion"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, we randomly choose a block from the 'pool', and insert the block within the circuit. Qubits that the block is applied on are also uniformly sampled. The parameters of the inserted set of gates are initialized to $0$ so that the new circuit can take the advantage of the previously optimized circuit since they act as identity. The code below inserts sets of gates into the circuit. The number of sets of gates depends on the hyperparameter **insert_rate**. Another hyperparameter **epsilon** is used to determine the variance from $0$ of initial parameters."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Circuit after insertion:\n",
      "--Rx(3.144)----Rz(3.512)----*----Rx(6.286)----Rz(3.039)----------------------------------------------------------------------------------------------------x--\n",
      "                            |                                                                                                                              |  \n",
      "----------------------------x----Rz(0.000)----Rx(0.000)----Rz(-0.00)----Rx(4.249)----Rz(3.141)----Rx(4.246)----Rz(5.477)----*------------------------------|--\n",
      "                                                                                                                            |                              |  \n",
      "--Rx(0.001)----Rz(5.499)----*-----------------------------------------------------------------------------------------------x----Rx(3.141)----Rz(2.688)----|--\n",
      "                            |                                                                                                                              |  \n",
      "----------------------------x----Rx(0.003)----Rz(2.362)----Rx(0.003)----Rz(1.500)--------------------------------------------------------------------------*--\n",
      "                                                                                                                                                              \n"
     ]
    }
   ],
   "source": [
    "# Insert indentity blocks into the current circuit, before doing so, need to\n",
    "# decompose the circuit from layers to gates\n",
    "new_cir = cir_decompose(vans.cir)\n",
    "new_cir = Inserter.insert_identities(new_cir, vans.insert_rate, vans.epsilon)\n",
    "\n",
    "# Print the updated circuit\n",
    "print(\"Circuit after insertion:\\n\" + str(new_cir))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Simplification"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Then we will simplify the new circuit. Rules are pretty simple:\n",
    "1. Combine consecutive $CNOT$ gates.\n",
    "2. Combine rotations gates.\n",
    "3. Remove $CNOT$ gates and $Rz$ gates in the front of the circuit if the initial state is $|0\\rangle$.\n",
    "4. Commute rotation gates and $CNOT$ gates if the circuit can be further simplified in doing so.\n",
    "\n",
    "Then we run the parameter optimization process again and get the new minimized value for the loss function. If the new value is smaller than the previous value or the difference between them is smaller than a value that depends on the hyperparameter **accept_wall**, we accpet the new circuit as the current circuit. After accepting the new circuit, we will remove gates that do not lower the loss or lower the loss within a **threshold**."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "c:\\Users\\v_zhanglei48\\Anaconda3\\envs\\pq_new\\lib\\site-packages\\paddle\\fluid\\dygraph\\math_op_patch.py:251: UserWarning: The dtype of left and right variables are not the same, left dtype is paddle.float64, but right dtype is paddle.float32, the right dtype will convert to paddle.float64\n",
      "  warnings.warn(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--Rx(3.144)----Rz(3.512)----*----Rx(6.286)----Rz(3.039)------------------------------------------------x--\n",
      "                            |                                                                          |  \n",
      "----------------------------x----Rz(5.962)----Rx(5.858)----Rz(9.426)----*------------------------------|--\n",
      "                                                                        |                              |  \n",
      "--Rx(0.001)----Rz(5.499)----*-------------------------------------------x----Rx(3.141)----Rz(2.688)----|--\n",
      "                            |                                                                          |  \n",
      "----------------------------x----Rz(3.604)----Rx(5.126)----Rz(4.462)-----------------------------------*--\n",
      "                                                                                                          \n",
      "iter: 20 loss: [-1.1040964]\n",
      "iter: 40 loss: [-1.1159219]\n",
      "iter: 60 loss: [-1.1165943]\n",
      "iter: 80 loss: [-1.116723]\n",
      "iter: 100 loss: [-1.1167536]\n",
      "iter: 120 loss: [-1.1167593]\n",
      "Accpet the new circuit!\n",
      "     start deleting gates\n",
      "         Deletion: reject deletion\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: reject deletion\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "      12  gates are deleted!\n"
     ]
    }
   ],
   "source": [
    "# Simplify the updated circuit according to the simplification rules\n",
    "new_cir = Simplifier.simplify_circuit(new_cir, vans.zero_init_state)\n",
    "\n",
    "# Print the simplied circuits\n",
    "print(new_cir)\n",
    "\n",
    "# Then optimize the simplified circuits\n",
    "itr_loss = vans.optimization(new_cir)\n",
    "\n",
    "# Calculate the change of loss\n",
    "relative_diff = (itr_loss - vans.loss) / np.abs(itr_loss)\n",
    "\n",
    "# If the loss is decreased or increased within a threshold, accept the new circuit\n",
    "if relative_diff <= 0 or np.random.random() <= np.exp(\n",
    "    -relative_diff * vans.accept_wall\n",
    "):\n",
    "    print(\"Accpet the new circuit!\")\n",
    "\n",
    "    # Remove gates that do not lower the loss\n",
    "    new_cir = vans.delete_gates(new_cir, itr_loss)\n",
    "    new_cir = Simplifier.simplify_circuit(new_cir, vans.zero_init_state)\n",
    "    itr_loss = loss_func(new_cir, *vans.loss_args)\n",
    "    vans.loss = itr_loss\n",
    "else:\n",
    "    print(\"Decline the new circuit!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Then we update the current circuit to the new circuit."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The current circuit:\n",
      "--Rx(3.141)----*----------------------x--\n",
      "               |                      |  \n",
      "---------------x----*-----------------|--\n",
      "                    |                 |  \n",
      "--------------------x----Rx(3.142)----|--\n",
      "                                      |  \n",
      "--------------------------------------*--\n",
      "                                         \n"
     ]
    }
   ],
   "source": [
    "# Update the current circuit\n",
    "vans.cir = new_cir\n",
    "\n",
    "print(\"The current circuit:\\n\" + str(vans.cir))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The insertion and simplification together forms one iteration for the architecture optimization process. The number of iterations is determined by the hyperparameter **iter_out**. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Simple version"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "While you can customize your own optimization process by adjusting insertion and simplification processes and implementing the training process, Paddle Quantum provides an elegant version of VAns. You can complete the architecture optimization all at once."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Out iteration 1 for structure optimization:\n",
      "iter: 20 loss: [-1.1167214]\n",
      "iter: 40 loss: [-1.1166948]\n",
      "iter: 60 loss: [-1.1167494]\n",
      "iter: 80 loss: [-1.116759]\n",
      "iter: 100 loss: [-1.1167591]\n",
      "iter: 120 loss: [-1.1167595]\n",
      " Current loss: [-1.1167595]\n",
      " Current cir:\n",
      "--Rx(3.141)----*----------------------x--\n",
      "               |                      |  \n",
      "---------------x----*-----------------|--\n",
      "                    |                 |  \n",
      "--------------------x----Rx(3.141)----|--\n",
      "                                      |  \n",
      "--------------------------------------*--\n",
      "                                          \n",
      "\n",
      "Out iteration 2 for structure optimization:\n",
      "iter: 20 loss: [-1.008932]\n",
      "iter: 40 loss: [-1.1085335]\n",
      "iter: 60 loss: [-1.133744]\n",
      "iter: 80 loss: [-1.1368372]\n",
      "iter: 100 loss: [-1.1372123]\n",
      "iter: 120 loss: [-1.1372803]\n",
      "     accpet the new circuit!\n",
      "     start deleting gates\n",
      "         Deletion: reject deletion\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: reject deletion\n",
      "         Deletion: reject deletion\n",
      "         Deletion: reject deletion\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: reject deletion\n",
      "      3  gates are deleted!\n",
      " Current loss: -1.1331816911697388\n",
      " Current cir:\n",
      "--Rx(3.143)----*------------------------------------------------------------------x--\n",
      "               |                                                                  |  \n",
      "---------------x----*----Rx(0.223)----Rz(2.485)----*--------*---------------------|--\n",
      "                    |                              |        |                     |  \n",
      "--------------------|------------------------------|--------x--------Rx(3.142)----|--\n",
      "                    |                              |                              |  \n",
      "--------------------x------------------------------x----Rz(0.458)-----------------*--\n",
      "                                                                                      \n",
      "\n",
      "Out iteration 3 for structure optimization:\n",
      "iter: 20 loss: [-0.37809896]\n",
      "iter: 40 loss: [-1.0471339]\n",
      "iter: 60 loss: [-1.1262195]\n",
      "iter: 80 loss: [-1.1365637]\n",
      "iter: 100 loss: [-1.1371676]\n",
      "iter: 120 loss: [-1.1372685]\n",
      "     accpet the new circuit!\n",
      "     start deleting gates\n",
      "         Deletion: reject deletion\n",
      "         Deletion: reject deletion\n",
      "         Deletion: reject deletion\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: reject deletion\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "      4  gates are deleted!\n",
      " Current loss: -1.1282802820205688\n",
      " Current cir:\n",
      "--Rx(3.143)----*----------------------------------------------------------x--\n",
      "               |                                                          |  \n",
      "---------------x----*----Rx(0.224)----Rz(2.252)----*----*-----------------|--\n",
      "                    |                              |    |                 |  \n",
      "--------------------|------------------------------|----x----Rx(3.146)----|--\n",
      "                    |                              |                      |  \n",
      "--------------------x------------------------------x----------------------*--\n",
      "                                                                              \n",
      "\n",
      "Out iteration 4 for structure optimization:\n",
      "iter: 20 loss: [-0.5603936]\n",
      "iter: 40 loss: [-1.0798837]\n",
      "iter: 60 loss: [-1.1325867]\n",
      "iter: 80 loss: [-1.1360469]\n",
      "iter: 100 loss: [-1.1370715]\n",
      "iter: 120 loss: [-1.1372685]\n",
      "     accpet the new circuit!\n",
      "     start deleting gates\n",
      "         Deletion: reject deletion\n",
      "         Deletion: reject deletion\n",
      "         Deletion: reject deletion\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: reject deletion\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "      9  gates are deleted!\n",
      " Current loss: -1.1321836709976196\n",
      " Current cir:\n",
      "--Rx(3.145)----*----------------------------------------------------------x--\n",
      "               |                                                          |  \n",
      "---------------x----*----Rx(0.228)----Rz(2.075)----*----*-----------------|--\n",
      "                    |                              |    |                 |  \n",
      "--------------------|------------------------------|----x----Rx(3.143)----|--\n",
      "                    |                              |                      |  \n",
      "--------------------x------------------------------x----------------------*--\n",
      "                                                                              \n",
      "\n",
      "Out iteration 5 for structure optimization:\n",
      "iter: 20 loss: [-1.136969]\n",
      "iter: 40 loss: [-1.1371931]\n",
      "iter: 60 loss: [-1.137272]\n",
      "iter: 80 loss: [-1.1372828]\n",
      "iter: 100 loss: [-1.1372837]\n",
      "iter: 120 loss: [-1.1372838]\n",
      "     accpet the new circuit!\n",
      "     start deleting gates\n",
      "         Deletion: reject deletion\n",
      "         Deletion: reject deletion\n",
      "         Deletion: reject deletion\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: accept deletion with acceptable loss\n",
      "         Deletion: reject deletion\n",
      "      2  gates are deleted!\n",
      " Current loss: -1.1372729539871216\n",
      " Current cir:\n",
      "--Rx(3.142)----*----------------------------------------------------------x--\n",
      "               |                                                          |  \n",
      "---------------x----*----Rx(0.226)----Rz(1.571)----*----*-----------------|--\n",
      "                    |                              |    |                 |  \n",
      "--------------------|------------------------------|----x----Rx(3.149)----|--\n",
      "                    |                              |                      |  \n",
      "--------------------x------------------------------x----------------------*--\n",
      "                                                                              \n",
      "\n",
      "\n",
      "\n",
      "The final loss: -1.1372729539871216\n",
      "The final circuit:\n",
      "--Rx(3.142)----*----------------------------------------------------------x--\n",
      "               |                                                          |  \n",
      "---------------x----*----Rx(0.226)----Rz(1.571)----*----*-----------------|--\n",
      "                    |                              |    |                 |  \n",
      "--------------------|------------------------------|----x----Rx(3.149)----|--\n",
      "                    |                              |                      |  \n",
      "--------------------x------------------------------x----------------------*--\n",
      "                                                                             \n"
     ]
    }
   ],
   "source": [
    "# optimization process all at once\n",
    "vans.train()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The final circuit we got from VAns is"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Final circuit:\n",
      "--Rx(3.142)----*----------------------------------------------------------x--\n",
      "               |                                                          |  \n",
      "---------------x----*----Rx(0.226)----Rz(1.571)----*----*-----------------|--\n",
      "                    |                              |    |                 |  \n",
      "--------------------|------------------------------|----x----Rx(3.149)----|--\n",
      "                    |                              |                      |  \n",
      "--------------------x------------------------------x----------------------*--\n",
      "                                                                             \n",
      "Final loss:\n",
      "-1.1372729539871216\n"
     ]
    }
   ],
   "source": [
    "print(\"Final circuit:\\n\" + str(vans.cir))\n",
    "print(\"Final loss:\\n\" + str(vans.loss))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Comparison with original VQE"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The circuit we got using VAns consists of only 5 parameters and the depth of the circuit is 9. The minimized loss value is $-1.13728392$ Ha. Comparing to the fixed ansatz used in the original VQE tutorial, where the circuit consists of 12 parameters and with depth 11, VAns reduces the number of parameters needed while keeps the circuit shallower. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "_______\n",
    "\n",
    "## References\n",
    "\n",
    "[1] Bilkis, M., et al. \"A semi-agnostic ansatz with variable structure for quantum machine learning.\" [arXiv preprint arXiv:2103.06712 (2021).](https://arxiv.org/abs/2103.06712)"
   ]
  }
 ],
 "metadata": {
  "interpreter": {
   "hash": "2ab84abaf8d5bbc8765aba8eb82d11e7069f2ff20e8f79b8a9cdeccefd2ac4da"
  },
  "kernelspec": {
   "display_name": "Python 3.8.13 ('pq_new')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
